/*
 *  World.cpp
 *  GLRender
 *
 *  Created by Patrick zulian on 5/4/10.
 *  Copyright 2010 University  of Lugano. All rights reserved.
 *
 */

#include "World.h"
#include "Base.h"
#include "Configuration.h"
#include "Utils.h"
#include "WeaponBonus.h"
#include "PlayerBonus.h"
#include "Planet.h"

void
World::init()
{
	particleSystem = new ParticleSystem(RGBd (1.0, 1.0, 1.0), 500, 100);
	lastCollisionCheck = 0.0; collisionCheckFreq = 6.0; timeElapsed = 0.0;
	debugInsideCollisionZone = false; newExplosion = false; playerHit = false; bigExplosion = false;
}


void
World::clear()
{
	utils::FreeValues< std::vector< WObject * > >(wObjects);
	utils::FreeValues< std::vector< WMovingObject *> >(wMvObjects);
	wObjects.clear(); wMvObjects.clear();
}

World::World()
{
	currentLevel=Level::getFirstLevel(this);
	collSys = new CollisionSystem();
	init();
	pause=false;
}

void World::checkBonus (WObject *o)
{
	srand(time(NULL));
	int n = rand() % 2;
	if (n != 0)
		return;
	n = rand() % 3;
	Bonus *b;
	switch (n) {
		case 0:
			b = new WeaponBonus(o->position(),RGBd(0,0,255), NULL,0.0,-8.0);
			break;
		case 1:
			b = new WeaponBonus(o->position(), RGBd(255,0,0), NULL,5.0,0.0);
			break;
		default:
			b = new PlayerBonus(o->position(), RGBd(0,255,0), 300);
	}
	wObjects.push_back(b);
}


void 
World::addExplosion(WObject * o)
{
	const double rad=o->getRadius();
	explosions.push_back(Explosion(o->position(),rad, (int)(rad*20.0),(int)(2.0*rad)));
	newExplosion = true;
	bigExplosion |= o->getRadius() > 80;
}

void
World::addFragmentation(WObject * o)
{
//	Asteroid * ast = dynamic_cast< Asteroid * >(o);
//	if (ast != NULL) {
//		fragmentations.push_back(Fragmentation(ast));
//		return;
//	}
//		
//	
//	Planet * planet = dynamic_cast< Planet * >(o);
//	if (planet == NULL) return;
//	
//	fragmentations.push_back(Fragmentation(planet));
	fragmentations.push_back(Fragmentation(o));
}

void World::eraseDeadObjects(){
	WObjIt iter = wObjects.begin();
	while( iter != wObjects.end() )
    {
		if (!(*iter)->alive()) {
			WObject * o = *iter;
			iter = wObjects.erase( iter );
			if(o->insideWorld() && !o->isBonus()) {
				if(inFront(o->position())) {
					addExplosion(o);
					addFragmentation(o);
				}
				checkBonus(o);
			}
			delete(o);
		}
		else
			++iter;
			    }
	
	
	WMvObjIt miter = wMvObjects.begin();
	while( miter != wMvObjects.end() )
    {
		if (!(*miter)->alive()) {
			WObject * o = *miter;
			miter = wMvObjects.erase( miter );
			delete(o);
		}
		else
			++miter;
    }
}

void
World::addFastObject (WMovingObject *w)
{
	wMvObjects.push_back(w);
}

void
World::addSlowObject (WMovingObject *w)
{
	wObjects.push_back(w);
}


void 
World::onGameStarted()
{
	SoundEngine::getInstance()->playMusic();
}

void
World::step(const double tau )
{
	if(pause)
		return;
	
	player->prepareForStep();
	
	if(!player->alive() && !Configuration::getInstance()->isDebugMode()){
		pause=true;
		return;
	}// WARNRING TO UNCOMMENT
	
	player->checkShoot(timeElapsed,tau);
	currentLevel->step(tau);
	
	if (hasToCheckCollision(tau)) {

		collSys->reset(player->position() - ( currentLevel->getSize() * 0.5 ), player->position() + ( currentLevel->getSize() * 0.5 ));
		for(WObjIt it = wObjects.begin(); it != wObjects.end(); it++)
		{
			if(!collSys->insert((*it))) {
				(*it)->insideWorld() = false;
				(*it)->alive() = false;
			}
		}
		//check player to object collisions
		playerHit = collSys->checkCollision(getPlayer()->getObject());
		//check object to object collisions
		collSys->checkCollisions(); 
		
	}
	
	
	//move weapon and check for weapon to objects collisions
	for (WMvObjIt it = wMvObjects.begin(); it != wMvObjects.end(); it++)
	{
		if (hasToCheckCollision(tau)) {
			if (collSys->isIn(*it)) {
				collSys->checkCollision(*it);
			} else {
				(*it)->alive() = false;
			}
		}
		

		(*it)->step(tau);

	}
	
	
	for(WObjIt it = wObjects.begin(); it != wObjects.end(); it++)
	{
		(*it)->step(tau);
	}

	scheduleColllision(tau);
	
	
	
	if(getPlayer()->hasWin()) {
		pause=true;
		
	} else {
		eraseDeadObjects();
		
		for(std::vector<Explosion>::iterator it = explosions.begin(); it != explosions.end(); ){
			if((*it).isFinished() || !inFront(it->getCenter())){
				it = explosions.erase(it);
			}
			else {
				it->step(tau);
				it++;
			}
		}
		
		for(std::vector<Fragmentation>::iterator it = fragmentations.begin(); it != fragmentations.end(); ){
			if((*it).isFinished() || !inFront(it->getCenter())){
				it = fragmentations.erase(it);
			}
			else {
				it->step(tau);
				it++;
			}
		}
		
	}
	timeElapsed += tau;
}

void 
World::goToNextLevel()
{
	setLevel(currentLevel->nextLevel());
}

void
World::goToFirstLevel(){
	player->resurrect();
	setLevel(Level::getFirstLevel(this));
}

void 
World::setLevel(Level * l){
	clear();
	currentLevel = l;
	player->setObjective(currentLevel->getObjective());
	player->position() = currentLevel->getStartPosition();
}

void
World::renderOn(AbstractRenderer *renderer)
{	

	if(pause)
		return;
	
	if( Configuration::getInstance()->isDebugMode() ) collSys->renderOn(renderer);
	
	for(WObjIt it = wObjects.begin(); it != wObjects.end(); it++)
	{
		if((*it)->alive() && inFront((*it)->position()))
			(*it)->renderOn(renderer);
	}
	
	for (WMvObjIt it = wMvObjects.begin(); it != wMvObjects.end(); it++)
	{
		if((*it)->alive() && inFront((*it)->position()))
			(*it)->renderOn(renderer);
	}
	
	for(std::vector<Explosion>::iterator it = explosions.begin(); it != explosions.end(); it++) {
			it->display();
	}
	
	for(std::vector<Fragmentation>::iterator it = fragmentations.begin(); it != fragmentations.end(); it++ )  {
		it->renderOn(renderer);
	}
	
	if(playerHit) {
		if (player->hasTakenPowerUp()) {
			renderer->renderFlash(RGBd(0.0, 0.4, 1.0), 100);
		} else {
			renderer->renderFlash(RGBd(1.0, 0.4, 0.3), 100);
		}
		playerHit = false;
	} else
	if (newExplosion) {
		if (bigExplosion) {
			renderer->renderFlash(RGBd(1.0, 1.0, 1.0), 250);
			bigExplosion = false;
		} else {
			renderer->renderFlash(RGBd(1.0, 1.0, 1.0));
		}
		newExplosion = false;
	}
	

	particleSystem->setCenter(player->position() );
	particleSystem->setDirection(-player->velocity());
	particleSystem->display();
	
	player->renderOn(renderer);
}

void
World::togglePause(){
	pause=!pause;
	
	if(player->hasWin())
		goToNextLevel();
	else if(!player->alive()) //TO UNCOMMENT
		goToFirstLevel();
}